package com.rattsoftware.client.widgets;

import java.util.ArrayList;
import java.util.List;

import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.Style.VerticalAlignment;
import com.extjs.gxt.ui.client.data.BasePagingLoader;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.extjs.gxt.ui.client.data.PagingLoader;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.HorizontalPanel;
import com.extjs.gxt.ui.client.widget.Label;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.VerticalPanel;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.ListField;
import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.rattsoftware.client.AppMessages;
import com.rattsoftware.client.controllers.ItemAccessController;

/**
 * ItemAccessGrid is used to create a DualFieldList, with search and paging functionality.
 * (A DualFieldList is two lists where items can be moved from one list to the other.)
 */
public class ItemAccessListField <M extends ModelData> extends LayoutContainer {
	protected final AppMessages appMessages = GWT.create(AppMessages.class);
	protected ItemAccessListFieldMessages messages;
	
	private ItemAccessController<M> controller;

    private int pageSize = 20;   
    
    private int listSize = 100;
    
    private PagingFilterListField fromField;
    private PagingFilterListField toField;
    
    private List<M> fromItems;
    private List<M> toItems;
    private int fromCounter;
    private int toCounter;


    public ItemAccessListField(ItemAccessListFieldMessages messages, ItemAccessController<M> controller) {
    	this.messages = messages;
        this.controller = controller;
        init();
    }
    
    public ItemAccessListField(ItemAccessListFieldMessages messages, ItemAccessController<M> controller, int listSize) {
    	this.messages = messages;
        this.controller = controller;
        this.listSize = listSize;
        init();
    }

    private void init() {
    	fromCounter=0;
    	toCounter=0;

        Button addUserBtn = new Button(messages.getAddOne());
        addUserBtn.setMinWidth(110);
        addUserBtn.addListener(Events.Select, new Listener<ButtonEvent>() {
            public void handleEvent(ButtonEvent be) {
            	onButtonRight(be);
            }
        });
        Button addAllUserBtn = new Button(messages.getAddAll());
        addAllUserBtn.setMinWidth(110);
        addAllUserBtn.addListener(Events.Select, new Listener<ButtonEvent>() {
            public void handleEvent(ButtonEvent be) {
            	onButtonAllRight(be);
            }
        });

        Button removeUserBtn = new Button(messages.getRemoveOne());
        removeUserBtn.setMinWidth(110);
        removeUserBtn.addListener(Events.Select, new Listener<ButtonEvent>() {
            public void handleEvent(ButtonEvent be) {
                onButtonLeft(be);
            }
        });
        Button removeAllUserBtn = new Button(messages.getRemoveAll());
        removeAllUserBtn.setMinWidth(110);
        removeAllUserBtn.addListener(Events.Select, new Listener<ButtonEvent>() {
            public void handleEvent(ButtonEvent be) {
                onButtonAllLeft(be);
            }
        });
        
        HorizontalPanel userTable = new HorizontalPanel();
        userTable.setVerticalAlign(VerticalAlignment.MIDDLE);
        userTable.setBorders(false);
        
        fromField = new PagingFilterListField(messages.getLeftHeading()) {
            void loadData(PagingToolBar pagingToolBar, PagingLoadConfig pagingLoadConfig, AsyncCallback<PagingLoadResult<M>> callback) {
    				controller.getUnMappedData(pagingLoadConfig, callback);
            }
        };
        userTable.add(fromField);
        
        VerticalPanel buttons = new VerticalPanel();
        buttons.setHorizontalAlign(HorizontalAlignment.CENTER);
        buttons.setBorders(false);
        buttons.add(addUserBtn);
        buttons.add(addAllUserBtn);
        buttons.add(new Label(""));
        buttons.add(removeUserBtn);
        buttons.add(removeAllUserBtn);
        userTable.add(buttons);

        toField = new PagingFilterListField(messages.getRightHeading()) {
            void loadData(PagingToolBar pagingToolBar, PagingLoadConfig pagingLoadConfig, AsyncCallback<PagingLoadResult<M>> callback) {
        		controller.getMappedData(pagingLoadConfig, callback);
            }
        };
        userTable.add(toField);
        add(userTable);
    }
    
    private void onButtonAllLeft(ButtonEvent be) {
        buttonLeft(toField.field.getStore().getModels());
    }

    private void onButtonLeft(ButtonEvent be) {
    	buttonLeft(toField.field.getSelection());
    }
    
     
	/**
	 * delete from right(to), add to left (from)
	 */
	private void buttonLeft(final List<M> sel) {
		final List<M> newSel = removeDisabledItems(sel);
		if (newSel.size() > 0) {
			toCounter = toCounter + newSel.size();
			fromCounter = fromCounter - newSel.size();
			toItems = newSel;
			fromItems = null;
			//ProgressIndicator.showProgressBar();
			Scheduler.get().scheduleDeferred(new ScheduledCommand() {
				public void execute() {
					try {
						controller.deleteMapping(newSel, ItemAccessListField.this);
					} catch (Exception e) {
						//ProgressIndicator.hideProgressBar();
						MessageBox.alert(appMessages.error(), e.getMessage(), null);
					}
				}
			});
		}
	}

    private void onButtonAllRight(ButtonEvent be) {
        buttonRight(fromField.field.getStore().getModels());
    }
    
    private void onButtonRight(ButtonEvent be) {
        buttonRight(fromField.field.getSelection());
	}
    
	/**
	 * add to right(to), delete from left(from)
	 */
	private void buttonRight(List<M> sel) {
		final List<M> newSel = removeDisabledItems(sel);
		if (newSel.size() > 0) {
			fromCounter = fromCounter + newSel.size();
			toCounter = toCounter - newSel.size();
			fromItems = newSel;
			toItems = null;
			//ProgressIndicator.showProgressBar();
			Scheduler.get().scheduleDeferred(new ScheduledCommand() {
				public void execute() {
					try {
						controller.addMapping(newSel, ItemAccessListField.this);
					} catch (Exception e) {
						//ProgressIndicator.hideProgressBar();
						MessageBox.alert(appMessages.error(), e.getMessage(), null);
					}
				}
			});
		}
	}

	private List<M> removeDisabledItems(List<M> sel) {
		List<M> cleanSel = new ArrayList<M>();
		for (M m : sel) {
			Boolean disabled = m.get("disabled");
			if (disabled == null || disabled == Boolean.FALSE) {
				cleanSel.add(m);
			}
		}
		return cleanSel;
	}

	public void refresh() {
		// manually add/remove items to/from the lists
		if (fromItems != null) {
			for (M model : fromItems) {
				fromField.field.getStore().remove(model);
				if (!toField.field.getStore().contains(model)) {
					toField.field.getStore().add(model);
				}
			}
		}
		if (toItems != null) {
			for (M model : toItems) {
				toField.field.getStore().remove(model);
				if (!fromField.field.getStore().contains(model)) {
					fromField.field.getStore().add(model);
				}
			}
		}
		if (doRefresh()) {
			// do a full (server side) refresh of data in the lists
			fromCounter = 0;
			fromField.refresh();
			toCounter = 0;
			toField.refresh();
		}
		//ProgressIndicator.hideProgressBar();
	}
	
	private boolean doRefresh() {
		if ((fromItems == null || fromItems.size() == 0)
				&& (toItems == null || toItems.size() == 0)) {
			// first time around
			return true;
		}
		if (fromCounter >= pageSize || toCounter >= pageSize) {
			// one of the counters is bigger than pageSize (indicating the other
			// is empty)
			return true;
		}
		if (!hasNonDisabledItem(toField.field.getStore().getModels())
				|| !hasNonDisabledItem(fromField.field.getStore().getModels())) {
			// one of the lists is empty (or has only disabled item(s) left)
			return true;
		}
		return false;
	}

	private boolean hasNonDisabledItem(List<M> models) {
		for (M m : models) {
			Boolean disabled = m.get("disabled");
			if (disabled == null || disabled == Boolean.FALSE) {
				return true;
			}
		}
		return false;
	}
    
    abstract class PagingFilterListField extends ContentPanel {
    	ListField<M> field = new ListField<M>();
        PagingToolBar pagingToolBar = new SmallPagingToolBar(pageSize);
        PagingLoader<PagingLoadResult<M>> loader;
        String filterValue;
        
        void loadData() {
        	loader.load();
        }
        
        PagingFilterListField(String heading) {
        	super();
        	
            setHeading(heading);
            setBorders(false);
            
            loader = new BasePagingLoader<PagingLoadResult<M>>(
                    new RpcProxy<PagingLoadResult<M>>() {
                     	@Override
						protected void load(Object loadConfig,
								final AsyncCallback<PagingLoadResult<M>> callback) {
							 GWT.log("ItemAccessListField load data");
	                            final PagingLoadConfig pagingLoadConfig = (PagingLoadConfig)loadConfig;
	                            pagingLoadConfig.set(RemoteStoreFilterField.PARM_QUERY, filterValue);
	                    		pagingLoadConfig.set(RemoteStoreFilterField.PARM_FIELD, "name");
	                            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
	                                public void execute() {
	                                	loadData(pagingToolBar, pagingLoadConfig, callback);
	                                }
	                            });
						}
                    }
            );
            
            loader.setRemoteSort(true);
            loader.setSortField("name");
            loader.setSortDir(SortDir.ASC);
            
            pagingToolBar.bind(loader);
            ListStore<M> store = new ListStore<M>(loader);
            
            // filter to search for users
            final RemoteStoreFilterField<M> filterField = new RemoteStoreFilterField<M> () {
               @Override
               protected void handleOnFilter(String filterValue) {
            	   // handle filtering - this is a call after each key pressed - it might be improved */
            	   PagingFilterListField.this.filterValue = filterValue;
            	   loader.load(0, pageSize);
               }
               
               @Override
               protected void handleCancelFilter () {
            	   PagingFilterListField.this.filterValue = null;
            	   loader.load(0, pageSize);
               }
            };
            filterField.setEmptyText(messages.getSearch());
            filterField.setWidth(185);
            filterField.bind(store);
            
            LayoutContainer bottomComponent = new LayoutContainer();
            bottomComponent.setBorders(false);
            bottomComponent.add(filterField);
            bottomComponent.add(pagingToolBar);
            setBottomComponent(bottomComponent);

            field.setStore(store);
            field.setBorders(false);
            field.setTemplate(getTemplate());
            field.setSize(185, listSize);
            field.getListView().setLoadingText(messages.getLoading());

            add(field);
        }
        
		private native String getTemplate() /*-{  
			return [ 
			'<tpl for=".">', 
			'<div class="x-combo-list-item" qtip="{name}" <tpl if="typeof disabled != \'undefined\' && disabled != null && disabled !=\'\' && disabled==true">style="color:grey"</tpl>>{name}</div>', 
			'</tpl>' 
			].join("");  
		}-*/;
        
        void refresh() {
        	pagingToolBar.enable();
        	pagingToolBar.unmask();
        	pagingToolBar.refresh();
        }
        
        abstract void loadData(PagingToolBar pagingToolBar, PagingLoadConfig pagingLoadConfig, AsyncCallback<PagingLoadResult<M>> callback);
        
    }
}